#include "Map.h"

#include "GameObjectManager.h"
#include "Player.h"
#include "Room.h"
#include "Spawner.h"
#include "Tile.h"

#include <string>
#include <fstream>


Map::Map():
	width(0),
	height(0)
{
}


Map::~Map()
{
	clear();
}


void Map::clear()
{
	// Deallocates memory.
	for (Tiles::iterator it = tiles.begin(); it != tiles.end(); ++it)
	{
		for (Row::iterator it1 = (*it).begin(); it1 != (*it).end(); ++it1)
		{
			delete *it1;
		}
	}

	tiles.clear(); // Deletes the whole nine yards.

	width  = 0;
	height = 0;
}


bool Map::loadFile(const char* filename, const std::string& playerName1, const std::string& playerName2)
{
	Spawner::getInstance()->setMap(this);
	
	GameObjectManager::getInstance()->destroyInstance();
	GameObjectManager::getInstance()->setMap(this);

	std::ifstream file(filename);

	bool hasPlayer = false;
	bool hasGate = false;

	std::vector<GameObject*> rooms;

	while (file.good())
	{
		// Get a line of the file
		std::string line;
		std::getline(file, line);

		// Add a new row to the map
		tiles.push_back(Row());

		// Largest row will be the width of the map.
		unsigned int tempWidth = 0;
		++height;

		for (unsigned int i = 0 ; i < line.size(); ++i)
		{
			// Add a tile and spawn an object on the tile
			char id = line[i];
			tiles.back().push_back(new Tile(i, height - 1));
			GameObject* spawnedObject = Spawner::getInstance()->spawn(id, i, height - 1);
			
			// Validity check.
			if (spawnedObject) 
			{
				switch (id)
				{
				case Spawner::ID_PlayerOne:
					static_cast<Player*>(spawnedObject)->setName(playerName1);
					hasPlayer = true;
					break;
				case Spawner::ID_PlayerTwo:
					static_cast<Player*>(spawnedObject)->setName(playerName2);
					hasPlayer = true;
					break;
				case Spawner::ID_Room:
					rooms.push_back(spawnedObject);
					break;
				case Spawner::ID_Gate:
					hasGate = true;
					break;
				}
			}

			tempWidth++;
		}

		// Store the width if it is larger than the current width.
		width = max(width, tempWidth);
	}

	height = tiles.size();

	// Distribute gatekey
	if (!rooms.empty())
	{
		static_cast<Room*>(rooms[rand() % rooms.size()])->addGateKey();
	}

	// Fill up all the rows to the be the same length as the width.
	for (unsigned int y = 0; y < tiles.size(); ++y)
	{
		for (unsigned int x = tiles[y].size(); x < width; ++x)
		{
			tiles[y].push_back(new Tile(x, y));
			GameObject* spawnedObject = Spawner::getInstance()->spawn(Spawner::ID_Empty, x, y);
		}
	}

	return hasPlayer && !rooms.empty() && hasGate;
}


void Map::draw() const
{
	for (Tiles::const_iterator it = tiles.begin(); it != tiles.end(); ++it)
	{
		for (Row::const_iterator it1 = (*it).begin(); it1 != (*it).end(); ++it1)
		{
			(*it1)->draw();
		}
	}
}



bool Map::isWithinBounds(unsigned int xDesired, unsigned int yDesired) const
{
	return xDesired >= 0    && yDesired >= 0      // lower bounds
		&& xDesired < width && yDesired < height; // upper bounds
}


const Map::Row& Map::operator[] (unsigned int i) 
{
	return tiles[i];
}


const Map::Row& Map::operator[] (unsigned int i) const 
{
	return tiles[i];
} 
